﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using JetBrains.Annotations;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;
using MugenMvvmToolkit.ViewModels;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif
#if !NETFX_CORE
using System.Windows.Navigation;
#else
using Windows.UI.Xaml.Navigation;
#endif
using NavigationContext = MugenMvvmToolkit.Models.NavigationContext;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the default implementation of <see cref="INavigationProvider" />.
    /// </summary>
    public class NavigationProvider : INavigationProvider
    {
        #region Fields

        private NavigationResult _activeOperation;
        private INavigationContext _currentContext;
        private IViewModel _waitViewModel;
        private bool _isSuspended;
        private NavigatingCancelEventArgs _args;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="NavigationProvider" /> class.
        /// </summary>
        public NavigationProvider([NotNull]INavigationService navigationService, [NotNull]IViewModelProvider viewModelProvider, [NotNull] IViewPageMappingProvider pageMappingProvider, [NotNull] IViewManager viewManager,
            [CanBeNull]IThreadManager threadManager, INavigationOperationResultPolicy operationResultPolicy = null, INavigationCachePolicy cachePolicy = null)
        {
            Should.NotBeNull(navigationService, "navigationService");
            Should.NotBeNull(viewModelProvider, "viewModelProvider");
            Should.NotBeNull(pageMappingProvider, "pageMappingProvider");
            Should.NotBeNull(viewManager, "viewManager");

            NavigationService = navigationService;
            ViewModelProvider = viewModelProvider;
            PageMappingProvider = pageMappingProvider;
            ViewManager = viewManager;
            ThreadManager = threadManager ?? GlobalSettings.DefaultThreadManager;
            OperationResultPolicy = operationResultPolicy;
            CachePolicy = cachePolicy;

            NavigationService.Navigated += NavigationServiceOnNavigated;
            NavigationService.Navigating += NavigationServiceOnNavigating;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the view manager.
        /// </summary>
        public IViewManager ViewManager { get; private set; }

        /// <summary>
        /// Gets the view model provider.
        /// </summary>
        public IViewModelProvider ViewModelProvider { get; private set; }

        /// <summary>
        /// Gets the page mapping provider
        /// </summary>
        public IViewPageMappingProvider PageMappingProvider { get; private set; }

        /// <summary>
        /// Gets the thread manager.
        /// </summary>
        public IThreadManager ThreadManager { get; private set; }

        /// <summary>
        ///     Gets the current context of navigation.
        /// </summary>
        protected INavigationContext CurrentContext
        {
            get { return _currentContext; }
        }

        /// <summary>
        ///     Gets the active view model.
        /// </summary>
        protected IView CurrentView
        {
            get { return NavigationService.CurrentContent as IView; }
        }

        #endregion

        #region Methods

        // ReSharper disable once UnusedParameter.Local
        private void ReNavigate(INavigationContext context, NavigatingCancelEventArgs args)
        {
            var navigableViewModel = CurrentViewModel as INavigableViewModel;
            if (navigableViewModel != null)
                navigableViewModel.OnNavigatedFrom(context);
            TryClearOperationResultCallback(context);
            if (CurrentViewModel != null)
                TryCacheViewModel(context, CurrentView, CurrentViewModel);
            if (_args != null)
            {
                _args.Cancel = false;
                return;
            }

            bool result = false;
#if WPF
            if (args.Content != null)
            {
                result = NavigationService.Navigate(args.Content, args.ExtraData);
            }
            else
#endif
            if (context.PageMapping != null)
            {
#if WPF
                    result = NavigationService.Navigate(context.PageMapping, args.ExtraData);
#endif
#if SILVERLIGHT
                result = NavigationService.Navigate(context.PageMapping);
#endif
#if NETFX_CORE
                result = NavigationService.Navigate(context.PageMapping, context.Parameter);
#endif
            }
            else if (context.Uri != null)
            {
#if WPF
                    result = NavigationService.Navigate(context.Uri, args.ExtraData);
#endif
#if SILVERLIGHT
                result = NavigationService.Navigate(context.Uri);
#endif
            }
            TryCompleteNavigation(null, result);
        }

        private bool HasViewModel(object content)
        {
            var view = content as IView;
            if (view == null || !(view.DataContext is IViewModel))
                return false;
            var viewModel = (IViewModel)view.DataContext;
            var pageMappingItem = FindMappingForView(content.GetType());
#if NETFX_CORE
            if (!pageMappingItem.ViewModelType.GetTypeInfo().IsGenericType)
#else
            if (!pageMappingItem.ViewModelType.IsGenericType)
#endif

                return viewModel.GetType() == pageMappingItem.ViewModelType;
            var vmType = viewModel.GetType();
#if NETFX_CORE
            if (!vmType.GetTypeInfo().IsGenericType)
#else
            if (!vmType.IsGenericType)
#endif
                return false;
            return vmType.GetGenericTypeDefinition() == pageMappingItem.ViewModelType.GetGenericTypeDefinition();
        }

        private void OnNavigating(NavigatingCancelEventArgs args)
        {
            if (_isSuspended)
                return;
            INavigationContext context = CreateNavigationContextFromArgs(args);
            _currentContext = context;
            var navigableViewModel = CurrentViewModel as INavigableViewModel;
            if (navigableViewModel == null)
            {
                ReNavigate(context, args);
                return;
            }

            args.Cancel = true;
#if TASKSUPPORT
            Task<bool> navigateTask = navigableViewModel.OnNavigatingFrom(context);
            navigateTask
                .ContinueWith(task =>
                {
                    TryCompleteNavigation(task, null);
                    if (!task.Result)
                    {
                        TryCompleteNavigation(null, false);
                        return;
                    }
                    ThreadManager.InvokeInUi(() =>
                    {
                        _isSuspended = true;
                        using (new ActionToken(o => ((NavigationProvider)o)._isSuspended = false, this))
                            ReNavigate(context, args);
                    });
                }, navigateTask.GetContinuationOptions())
                .WithTaskExceptionHandler(navigableViewModel);
#else
            var operationResult = navigableViewModel.OnNavigatingFrom(context);
            operationResult.BeginWait(result =>
                 {
                     TryCompleteNavigation(result, null);
                     if (!result.Result)
                     {
                         TryCompleteNavigation(null, false);
                         return;
                     }
                     ThreadManager.InvokeInUi(() =>
                     {
                         _isSuspended = true;
                         using (new ActionToken(o => ((NavigationProvider)o)._isSuspended = false, this))
                             ReNavigate(context, args);
                     });
                 });
            operationResult.WithTaskExceptionHandler(navigableViewModel);
#endif
        }

        private IViewModel OnNavigated(ref INavigationContext context, NavigationEventArgs args, IView view)
        {
            if (_waitViewModel != null)
            {
                ViewManager.InitializeView(_waitViewModel, view);
                return _waitViewModel;
            }
            //To clear cache.
            IViewModel vm = TryTakeViewModelFromCache(context, view);

            if (!context.IsAutoCreatedViewModel)
                return (IViewModel)view.DataContext;

            if (vm != null)
            {
                ViewManager.InitializeView(vm, view);
                context = CreateNavigationContextFromArgs(args);
                return vm;
            }

            if (!OnNavigatingWithoutViewModelEvent(context, view))
            {
                NavigationService.NavigateToRoot();
                return null;
            }

            IViewPageMappingItem mappingItem = context.PageMapping ?? FindMappingForView(view.GetType());
            Should.BeViewModel(mappingItem.ViewModelType, "pageViewModelType", typeof(IViewModel));
            vm = ViewModelProvider.GetViewModel(mappingItem.ViewModelType, ActivationPolicy.WithoutView);
            ViewManager.InitializeView(vm, view);
            return vm;
        }

        private IViewPageMappingItem FindMappingForView(Type viewType)
        {
            IViewPageMappingItem viewPageMappingItem = PageMappingProvider.FindMappingForView(viewType, true);
            if (viewPageMappingItem == null)
                Thrower.ViewModelNotFound(viewType.Name);
            return viewPageMappingItem;
        }

        private IViewPageMappingItem FindMappingForViewModel(Type viewModelType, string viewName)
        {
            IViewPageMappingItem viewPageMappingItem = PageMappingProvider.FindMappingForViewModel(viewModelType,
                viewName, true);
            if (viewPageMappingItem == null)
                Thrower.ViewNotFound(viewModelType);
            return viewPageMappingItem;
        }

        /// <summary>
        ///     Registers a callback to complete the result operation.
        /// </summary>
        /// <param name="targetViewModel">The navigated view model.</param>
        /// <param name="navigationResult">The current navigation result.</param>
        /// <param name="context">The current navigation context.</param>
        private void RegisterOperationResultCallback(IViewModel targetViewModel,
            INavigationResult navigationResult, INavigationContext context)
        {
            if (OperationResultPolicy != null)
                OperationResultPolicy.RegisterOperationResultCallback(targetViewModel, navigationResult, context);
        }

        /// <summary>
        ///     Tries to clear the operation result callback.
        /// </summary>
        private void TryClearOperationResultCallback(INavigationContext context)
        {
            if (OperationResultPolicy != null)
                OperationResultPolicy.TryClearOperationResultCallback(CurrentViewModel, context);
        }

        /// <summary>
        ///     Tries to save a view model in the cache.
        /// </summary>
        private void TryCacheViewModel(INavigationContext context, IView view, IViewModel viewModel)
        {
            if (CachePolicy != null)
                CachePolicy.TryCacheViewModel(context, view, viewModel);
        }

        /// <summary>
        ///     Tries to get view model from the cache, and delete it from the cache.
        /// </summary>
        private IViewModel TryTakeViewModelFromCache(INavigationContext context, IView view)
        {
            if (CachePolicy != null)
                return CachePolicy.TryTakeViewModelFromCache(context, view);
            return null;
        }

        /// <summary>
        ///     Initializes a new context for navigation.
        /// </summary>
        protected void InitializeNewNavigation(NavigationResult navigationResult, INavigationContext context)
        {
            TryCompleteNavigation(null, false);
            _activeOperation = navigationResult;
            _currentContext = context;
        }

#if TASKSUPPORT
        /// <summary>
        ///     Tries to complete the current navigation task.
        /// </summary>
        /// <param name="task">An instance of task.</param>
        /// <param name="result">The result value, if any</param>
        protected void TryCompleteNavigation(Task task, bool? result)
        {
            NavigationResult activeOperation = _activeOperation;
            if (activeOperation == null)
                return;
            activeOperation.TryCompleteNavigation(task, result, ref _currentContext);
        }
#else
        /// <summary>
        /// Tries to complete the current navigation task.
        /// </summary>
        /// <param name="operation">An instance of operation.</param>
        /// <param name="result">The result value, if any</param>
        protected void TryCompleteNavigation(IOperationResult operation, bool? result)
        {
            var activeOperation = _activeOperation;
            if (activeOperation == null)
                return;
            activeOperation.TryCompleteNavigation(operation, result, ref _currentContext);
        }
#endif

#if WPF
        /// <summary>
        ///     Creates an instance of <see cref="INavigationContext" /> from a navigation event args.
        /// </summary>
        /// <param name="args">The specified navigation event args.</param>
        /// <returns>An instance of <see cref="INavigationContext" /></returns>
        protected virtual INavigationContext CreateNavigationContextFromArgs(EventArgs args)
        {
            if (_currentContext != null && !_currentContext.IsAutoCreatedViewModel)
                return _currentContext;
            var eventArgs = args as NavigatingCancelEventArgs;
            if (eventArgs != null)
            {
                if (eventArgs.Content != null)
                {
                    IViewPageMappingItem item = FindMappingForView(eventArgs.Content.GetType());
                    IDictionary<string, string> uriParameters = eventArgs.GetUriParameters();
                    return new NavigationContext(item, item.Uri.MergeUri(uriParameters), uriParameters,
                        eventArgs.ExtraData, !HasViewModel(eventArgs.Content), this, eventArgs.NavigationMode);
                }
                return new NavigationContext(null, eventArgs.Uri, eventArgs.GetUriParameters(), eventArgs.ExtraData,
                    true, this, eventArgs.NavigationMode);
            }

            var navigatedArgs = (NavigationEventArgs)args;
            if (navigatedArgs.Content != null)
            {
                IViewPageMappingItem item = FindMappingForView(navigatedArgs.Content.GetType());
                IDictionary<string, string> uriParameters = navigatedArgs.GetUriParameters();
                return new NavigationContext(item, item.Uri.MergeUri(uriParameters), uriParameters,
                    navigatedArgs.ExtraData, !HasViewModel(navigatedArgs.Content), this, null);
            }
            return new NavigationContext(null, navigatedArgs.Uri, navigatedArgs.GetUriParameters(),
                navigatedArgs.ExtraData, true, this, null);
        }
#endif

#if SILVERLIGHT
        /// <summary>
        /// Creates an instance of <see cref="INavigationContext"/> from a navigation event args.
        /// </summary>
        /// <param name="args">The specified navigation event args.</param>
        /// <returns>An instance of <see cref="INavigationContext"/></returns>
        protected virtual INavigationContext CreateNavigationContextFromArgs(EventArgs args)
        {
            if (_currentContext != null && !_currentContext.IsAutoCreatedViewModel)
                return _currentContext;
            var eventArgs = args as NavigatingCancelEventArgs;
            if (eventArgs != null)
                return new NavigationContext(null, eventArgs.Uri, eventArgs.GetUriParameters(), true, this, eventArgs.NavigationMode);

            var navigatedArgs = (NavigationEventArgs)args;
#if WINDOWS_PHONE
            NavigationMode navigationMode = navigatedArgs.NavigationMode;
#else
            NavigationMode? navigationMode = null;
#endif
            if (navigatedArgs.Content == null)
                // ReSharper disable once ExpressionIsAlwaysNull
                return new NavigationContext(null, navigatedArgs.Uri, navigatedArgs.GetUriParameters(), true, this, navigationMode);
            var item = FindMappingForView(navigatedArgs.Content.GetType());
            var uriParameters = navigatedArgs.GetUriParameters();
            // ReSharper disable once ExpressionIsAlwaysNull
            return new NavigationContext(item, item.Uri.MergeUri(uriParameters), uriParameters, !HasViewModel(navigatedArgs.Content), this, navigationMode);
        }
#endif

#if NETFX_CORE
        /// <summary>
        /// Creates an instance of <see cref="INavigationContext"/> from a navigation event args.
        /// </summary>
        /// <param name="args">The specified navigation event args.</param>
        /// <returns>An instance of <see cref="INavigationContext"/></returns>
        protected virtual INavigationContext CreateNavigationContextFromArgs(object args)
        {
            if (_currentContext != null && !_currentContext.IsAutoCreatedViewModel)
                return _currentContext;
            var eventArgs = args as NavigatingCancelEventArgs;
            if (eventArgs != null)
            {
                var item = FindMappingForView(eventArgs.SourcePageType);
                return new NavigationContext(item, null, true, this, eventArgs.NavigationMode);
            }

            var navigatedArgs = (NavigationEventArgs)args;
            if (navigatedArgs.Content != null)
            {
                var item = FindMappingForView(navigatedArgs.Content.GetType());
                return new NavigationContext(item, navigatedArgs.Parameter,
                    !HasViewModel(navigatedArgs.Content), this, navigatedArgs.NavigationMode);
            }
            else
            {
                var item = FindMappingForView(navigatedArgs.SourcePageType);
                return new NavigationContext(item, navigatedArgs.Parameter, true, this, navigatedArgs.NavigationMode);
            }
        }
#endif

        /// <summary>
        ///     Invokes the <see cref="NavigatingWithoutView" /> event.
        /// </summary>
        protected void OnNavigatingWithoutView(NavigationEventArgs args)
        {
            Action<INavigationProvider, NavigationEventArgs> handler = NavigatingWithoutView;
            if (handler != null)
                handler(this, args);
        }

        /// <summary>
        ///     Invokes the <see cref="Navigated" /> event.
        /// </summary>
        protected void OnNavigatedEvent(INavigationContext arg1, IViewModel arg2)
        {
            Action<INavigationProvider, INavigationContext, IViewModel> handler = Navigated;
            if (handler != null)
                handler(this, arg1, arg2);
        }

        /// <summary>
        ///     Invokes the <see cref="NavigatingWithoutViewModel" /> event.
        /// </summary>
        protected bool OnNavigatingWithoutViewModelEvent(INavigationContext context, IView view)
        {
            Action<INavigationProvider, INavigationContext, IView, CancelEventArgs> eventInvoke =
                NavigatingWithoutViewModel;
            if (eventInvoke != null)
            {
                var cancelEventArgs = new CancelEventArgs(false);
                eventInvoke(this, context, view, cancelEventArgs);
                return !cancelEventArgs.Cancel;
            }
            return true;
        }

        /// <summary>
        ///     Subcriber to the Navigating event.
        /// </summary>
        protected virtual void NavigationServiceOnNavigating(object sender,
            NavigatingCancelEventArgs navigatingCancelEventArgs)
        {
            try
            {
                _args = navigatingCancelEventArgs;
                OnNavigating(navigatingCancelEventArgs);
            }
            finally
            {
                _args = null;
            }
        }

        /// <summary>
        ///     Subcriber to the Navigated event.
        /// </summary>
        protected virtual void NavigationServiceOnNavigated(object sender, NavigationEventArgs e)
        {
            if (e.Content == null) return;
            var pageView = e.Content as IView;
            if (pageView == null)
            {
                OnNavigatingWithoutView(e);
                return;
            }
            INavigationContext context = CreateNavigationContextFromArgs(e);
            _currentContext = context;
            try
            {
                IViewModel viewModel = OnNavigated(ref context, e, pageView);
                _currentContext = context;
                if (viewModel != null)
                {
                    var navVm = viewModel as INavigableViewModel;
                    if (navVm != null)
                        navVm.OnNavigatedTo(context);
                    OnNavigatedEvent(context, viewModel);
                    TryCompleteNavigation(null, true);

                    NavigationResult activeOperation = _activeOperation;
                    if (activeOperation != null)
                        RegisterOperationResultCallback(viewModel, activeOperation, context);
                }
                else
                    TryCompleteNavigation(null, false);
            }
            finally
            {
                _activeOperation = null;
                _currentContext = null;
                _waitViewModel = null;
            }
        }

        #endregion

        #region Implementation of INavigationProvider

        /// <summary>
        ///     Gets the <see cref="INavigationService" />.
        /// </summary>
        public INavigationService NavigationService { get; private set; }

        /// <summary>
        /// Gets the cache policy, if any.
        /// </summary>
        public INavigationCachePolicy CachePolicy { get; private set; }

        /// <summary>
        /// Gets the operation result policy, if any.
        /// </summary>
        public INavigationOperationResultPolicy OperationResultPolicy { get; private set; }

        /// <summary>
        ///     Gets the active view model.
        /// </summary>
        public virtual IViewModel CurrentViewModel
        {
            get
            {
                var view = NavigationService.CurrentContent as IView;
                if (view == null)
                    return null;
                return view.DataContext as IViewModel;
            }
        }

#if SILVERLIGHT
        /// <summary>
        ///     Navigates to the specified  uri.
        /// </summary>
        /// <param name="uri">
        ///     The specified <see cref="Uri" />.
        /// </param>
        /// <param name="uriParameters">The specified uri-parameters, if any.</param>
        public virtual INavigationResult Navigate(Uri uri, IDictionary<string, string> uriParameters)
        {
            Should.NotBeNull(uri, "uri");
            var result = new NavigationResult();
            ThreadManager.InvokeInUi(() =>
                                      {
                                          uri = uri.MergeUri(uriParameters);
                                          InitializeNewNavigation(result, new NavigationContext(null, uri, uriParameters, false, this, NavigationMode.New));
                                          NavigationService.Navigate(uri);
                                      });
            return result;
        }

        /// <summary>
        ///     Navigates to the specified view model.
        /// </summary>
        /// <param name="viewModel">
        ///     The specified <see cref="IViewModel" />.
        /// </param>
        /// <param name="uriParameters">The specified uri-parameters, if any.</param>
        /// <param name="viewName">The specified view binding name, if any.</param>
        public virtual INavigationResult Navigate(IViewModel viewModel, IDictionary<string, string> uriParameters, string viewName = null)
        {
            Should.NotBeNull(viewModel, "viewModel");
            if (viewModel == CurrentViewModel)
                return NavigationResult.TrueNavigationResult;

            var result = new NavigationResult();
            var mappingItem = FindMappingForViewModel(viewModel.GetType(), viewName);
            if (uriParameters != null && uriParameters.Count != 0)
                mappingItem = mappingItem.MergeUri(uriParameters);
            ThreadManager.InvokeInUi(() =>
                                      {
                                          _waitViewModel = viewModel;
                                          InitializeNewNavigation(result, new NavigationContext(mappingItem, null, null, false, this, NavigationMode.New));
                                          NavigationService.Navigate(mappingItem);
                                      });
            return result;
        }
#endif

#if WPF
        /// <summary>
        ///     Navigates to the specified uri.
        /// </summary>
        /// <param name="uri">
        ///     The specified <see cref="Uri" />.
        /// </param>
        /// <param name="parameter">
        ///     A <see cref="T:System.Object" /> that contains data to be used for processing during
        ///     navigation.
        /// </param>
        /// <param name="uriParameters">The specified uri-parameters, if any.</param>
        public virtual INavigationResult Navigate(Uri uri, object parameter, IDictionary<string, string> uriParameters)
        {
            Should.NotBeNull(uri, "uri");
            var tcs = new NavigationResult();
            ThreadManager.InvokeInUi(() =>
            {
                uri = uri.MergeUri(uriParameters);
                InitializeNewNavigation(tcs,
                    new NavigationContext(null, uri, uriParameters, parameter, false, this, NavigationMode.New));
                NavigationService.Navigate(uri, parameter);
            });
            return tcs;
        }

        /// <summary>
        ///     Navigates to the specified view model.
        /// </summary>
        /// <param name="viewModel">
        ///     The specified <see cref="IViewModel" />.
        /// </param>
        /// <param name="parameter">
        ///     A <see cref="T:System.Object" /> that contains data to be used for processing during
        ///     navigation.
        /// </param>
        /// <param name="uriParameters">The specified uri-parameters, if any.</param>
        /// <param name="viewName">The specified view binding name, if any.</param>
        public virtual INavigationResult Navigate(IViewModel viewModel, IDictionary<string, string> uriParameters,
            object parameter, string viewName = null)
        {
            Should.NotBeNull(viewModel, "viewModel");
            if (viewModel == CurrentViewModel)
                return NavigationResult.TrueNavigationResult;

            var tcs = new NavigationResult();
            IViewPageMappingItem mappingItem = FindMappingForViewModel(viewModel.GetType(), viewName);
            if (uriParameters != null && uriParameters.Count != 0)
                mappingItem = mappingItem.MergeUri(uriParameters);
            ThreadManager.InvokeInUi(() =>
            {
                _waitViewModel = viewModel;
                InitializeNewNavigation(tcs,
                    new NavigationContext(mappingItem, null, uriParameters, parameter, false, this, NavigationMode.New));
                NavigationService.Navigate(mappingItem, parameter);
            });
            return tcs;
        }
#endif

#if NETFX_CORE
        /// <summary>
        ///     Navigates to the specified view model.
        /// </summary>
        /// <param name="viewModel">
        ///     The specified <see cref="IViewModel" />.
        /// </param>
        /// <param name="parameter">A <see cref="T:System.Object"/> that contains data to be used for processing during navigation.</param>
        /// <param name="viewName">The specified view binding name, if any.</param>
        public virtual INavigationResult Navigate(IViewModel viewModel, object parameter, string viewName = null)
        {
            Should.NotBeNull(viewModel, "viewModel");
            if (viewModel == CurrentViewModel)
                return NavigationResult.TrueNavigationResult;

            var tcs = new NavigationResult();
            var mappingItem = FindMappingForViewModel(viewModel.GetType(), viewName);
            ThreadManager.InvokeInUi(() =>
            {
                _waitViewModel = viewModel;
                InitializeNewNavigation(tcs, new NavigationContext(mappingItem, parameter, false, this, NavigationMode.New));
                NavigationService.Navigate(mappingItem, parameter);
            });
            return tcs;
        }
#endif

        /// <summary>
        ///     Occurs when the page has not implemented the <see cref="IView" /> interface.
        /// </summary>
        public virtual event Action<INavigationProvider, NavigationEventArgs> NavigatingWithoutView;

        /// <summary>
        ///     Occurs when the page has been refreshed or user has gone to the page through a web browser, or somewhere else.
        /// </summary>
        public virtual event Action<INavigationProvider, INavigationContext, IView, CancelEventArgs> NavigatingWithoutViewModel;

        /// <summary>
        ///     Occurs after page was navigated.
        /// </summary>
        public virtual event Action<INavigationProvider, INavigationContext, IViewModel> Navigated;

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            NavigationService.Navigating -= NavigationServiceOnNavigating;
            NavigationService.Navigated -= NavigationServiceOnNavigated;
        }

        #endregion
    }
}